package oceanserv

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/gorilla/websocket"
	"log"
	"ocean"
	"rhelper"
)

const (
	CEVENT_MSG            = 1
	CEVENT_LEAVE          = 2
	CEVENT_GW_BREAK       = 3
	CEVENT_HELPER_TIMEOUT = 4
)

type ControllerEvent struct {
	Event  int
	Id     int32
	Tunnel *ocean.Tunnel
}

type Controller struct {
	id      int32
	gwId    string
	ctype   int32
	apptype int32
	seq     int32
	chanx   chan *ControllerEvent
}

func (c *Controller) Send(cmd int32, msg []byte) bool {
	gwId := c.gwId
	g, ok := gateways[gwId]
	if !ok {
		log.Println("Send failed, no gateway:", gwId)
		return false
	}

	tunnel := &ocean.Tunnel{}
	tunnel.Target = &c.id
	tunnel.Cmd = &cmd
	tunnel.Data = msg

	c.seq++
	seq := c.seq
	tunnel.Seq = &seq

	buf, err := proto.Marshal(tunnel)
	if err != nil {
		log.Println("Send failed:", err)
		return false
	}

	ws := g.ws
	err = ws.WriteMessage(websocket.BinaryMessage, buf)
	if err != nil {
		log.Println("Send failed:", err)
		return false
	}

	return true
}

func (c *Controller) IsBusy() bool {
	return c.chanx != nil
}

func (c *Controller) Attach(chanx chan *ControllerEvent) bool {
	if c.chanx != nil {
		return false
	}

	c.chanx = chanx
	return true
}

func (c *Controller) Detach() {
	c.chanx = nil
}

func (c *Controller) GetId() int32 {
	return c.id
}

func (c *Controller) GetGWId() string {
	return c.gwId
}

func (c *Controller) GetType() int32 {
	return c.ctype
}

func (c *Controller) GetAppType() int32 {
	return c.apptype
}

func (c *Controller) onLeave() {
	if c.chanx == nil {
		return
	}

	e := &ControllerEvent{Event: CEVENT_LEAVE}
	e.Id = int32(c.id)
	c.chanx <- e
}

func (c *Controller) onConnectionBreak() {
	if c.chanx == nil {
		return
	}

	e := &ControllerEvent{Event: CEVENT_GW_BREAK}
	e.Id = int32(c.id)
	c.chanx <- e
}

func (c *Controller) onMessage(tunnel *ocean.Tunnel) {
	if c.chanx == nil {
		log.Println("Controller onMessage no listener, drop!")
		return
	}

	e := &ControllerEvent{Event: CEVENT_MSG, Tunnel: tunnel}
	e.Id = int32(c.id)
	c.chanx <- e
}

func (c *Controller) save2DB() {
	conn := rhelper.Pool().Get()
	defer conn.Close()

	_, err := conn.Do("HMSET", fmt.Sprintf("%s%d", rhelper.ControllerHash, int(c.id)),
		"id", c.id, "gwId", c.gwId, "type", c.ctype, "apptype", c.apptype)

	if err != nil {
		log.Println("controller save2DB failed:", err)
	}
}
